/*
 * W25Qxx.h
 *
 *  Created on: Jan 07, 2025
 *      Author: illi Eisenberg
 * 
 * 	引脚信息：
 * 			DI:	PB5
 * 			DO:	PB4
 * 			SCL:PB3
 * 			CS: PA3
 * 
 */

#ifndef INC_W25QXX_H_
#define INC_W25QXX_H_


// debug option (comment this line to disable the debug option
#define DEBUG_ON

// Device status:
#define W25Qx_OK 0x00
#define W25Qx_ERROR 0x01
#define W25Qx_BUSY 0x02
#define W25Qx_TIMEOUT 0x03

// Device ID:
#define WINBONS_SER_FLASH 0xEF //
#define W25Q05 0x10			   // 512k		    256 p	16
#define W25Q10 0x11			   //   1M		    512 p	17
#define W25Q20 0x12			   //   2M		   1024 p	18
#define W25Q40 0x13			   //   4M		   2048 p	19
#define W25Q80 0x14			   //   8M		   4096 p	20
#define W25Q16 0x15			   //  16M		   8192 p	21
#define W25Q32 0x16			   //  32M		  16384 p	22
#define W25Q64 0x17			   //  64M		  32768 p	23
#define W25Q128 0x18		   // 128M		  65536 p	24
#define W25Q256 0x19		   // 256M		 131072 p	25
#define W25Q512 0x20		   // 512M		 262144 p	26
#define W25Q01 0x21			   //   1G		 524288 p	27
#define W25Q02 0x22			   //   2G		1048576 p	28

// sector     = 16 pages = 4096 bytes
// numPAGES   = 2 ^ (DEV_SIZE_ID - 8)
// numSECTORS = numPAGES / 16
// numBLOCK   = numSECTORS / 16

// 扇区     = 16 页 = 4096 字节
// 页数量   = 2 ^ (设备容量ID - 8)
// 扇区数量 = 页数量 / 16
// 块数量   = 扇区数量 / 16
// there are 128 pages block, and 256 pages block

typedef struct
{
	uint8_t FLASH_Id;	  // Flash设备ID
	uint32_t FLASH_Size;  // Flash总容量(字节)
	uint32_t PAGE_SIZE;	  // 页大小(256字节)
	uint32_t SECTOR_SIZE; // 扇区大小(65536字节,0x10000)

	uint32_t BLOCK_SIZE;	  // 块大小(字节)
	uint32_t PAGE_QUANTITY;	  // 总页数
	uint32_t SECTOR_QUANTITY; // 总扇区数
	uint32_t BLOCK_QUANTITY;  // 总块数
} W25Qx_Parameter;

extern W25Qx_Parameter W25Qx_Param;

#define STATUS_REG_1_RD 0x05 // 状态寄存器1读取
#define STATUS_REG_2_RD 0x35 // 状态寄存器2读取
#define STATUS_REG_3_RD 0x15 // 状态寄存器3读取

#define W25Q_WIP 0x01 // 写入进行中

// 复位操作
#define W25Q_Enable_Reset 0x66 // 使能复位
#define W25Q_RReset 0x99	   // 复位设备
#define W25Q_POWER_DOWN 0xB9   // 进入掉电模式

// 识别操作
#define WINBOND_SERIAL_FLASH 0xEF // 华邦串行闪存

#define READ_DEVICE_ID 0x90 // 读取设备ID
#define JEDEC_ID 0x9F		// JEDEC ID
#define MEM_TYPE 0x40		// 存储器类型
#define MEM_CAPACITY 0x17	// 存储器容量 64Mb

// 写操作
#define WRITE_ENABLE 0x06  // 写使能
#define WRITE_DISABLE 0x04 // 写禁止

// 读操作
#define READ_DATA_3Badd 0x03	  // 使用3字节地址读取数据
#define READ_DATA_4Badd 0x13	  // 使用4字节地址读取数据
#define READ_DATA_FAST_3Badd 0x0B // 使用3字节地址快速读取数据
#define READ_DATA_FAST_4Badd 0x0C // 使用4字节地址快速读取数据

// 编程操作
#define PAGE_PROGRAM_3Badd 0x02 // 使用3字节地址页编程
#define PAGE_PROGRAM_4Badd 0x12 // 使用4字节地址页编程

// 擦除操作
#define SECTOR_ERASE_3Badd 0x20 // 使用3字节地址扇区擦除
#define SECTOR_ERASE_4Badd 0x21 // 使用4字节地址扇区擦除

#define BLOCK_ERASE_3Badd 0xD8 // 使用3字节地址块擦除
#define BLOCK_ERASE_4Badd 0xDC // 使用4字节地址块擦除

#define CHIP_ERASE 0xC7 // 芯片擦除

// 标志状态寄存器
#define W25Qxx_FSR_BUSY 0x01 // 忙状态
#define W25Qxx_FSR_WREN 0x02 // 写使能
#define W25Qxx_FSR_QE 0x02	 // 四线使能

#define W25Qxx_TIMEOUT 1000 // 超时时间

// Functions Definitions:

/**
 * @brief 初始化W25Qxx Flash存储器
 * @return uint8_t 返回初始化状态，0表示成功，非0表示失败
 */
uint8_t W25Q_Init(void);

/**
 * @brief 复位W25Qxx Flash存储器
 */
void W25Q_Reset(void);

/**
 * @brief 读取W25Qxx Flash存储器ID
 * @return uint8_t 返回读取状态，0表示成功，非0表示失败
 */
uint8_t W25Q_ReadID(void);

uint8_t W25Q_Get_Param(W25Qx_Parameter *Param);

/**
 * @brief 读取状态寄存器1
 * @return uint8_t 状态寄存器1的值
 * 
 * @note 状态寄存器1包含以下位信息：
 *       - Bit 0 (BUSY): 忙状态位，1表示设备忙，0表示设备空闲
 *       - Bit 1 (WEL): 写使能锁定位，1表示写使能，0表示写禁止
 *       - Bit 2 (BP0): 块保护位0
 *       - Bit 3 (BP1): 块保护位1  
 *       - Bit 4 (BP2): 块保护位2
 *       - Bit 5 (TB): 顶部/底部保护位，0表示顶部保护，1表示底部保护
 *       - Bit 6 (SEC): 扇区/块保护位，0表示扇区保护，1表示块保护
 *       - Bit 7 (SRP0): 状态寄存器保护位0
 */
uint8_t W25Q_Read_Status_Register_1(void);

/**
 * @brief 从指定页面读取数据
 * @param startPage 起始页面号
 * @param offset 页面内偏移量
 * @param size 读取数据大小
 * @param rData 读取数据缓冲区指针
 */
void W25Q_Read(uint32_t startPage, uint8_t offset, uint32_t size, uint8_t *rData);

/**
 * @brief 从指定页面快速读取数据
 * @param startPage 起始页面号
 * @param offset 页面内偏移量
 * @param size 读取数据大小
 * @param rData 读取数据缓冲区指针
 */
void W25Q_Read_Fast(uint32_t startPage, uint8_t offset, uint32_t size, uint8_t *rData);

/**
 * @brief 擦除指定扇区
 * @param numSector 扇区号
 */
void W25Q_Erase_Sector(uint16_t numSector);

/**
 * @brief 擦除指定64KB块
 * @param numBlock 块号
 */
void W25Q_Erase_Block_64KB(uint32_t numBlock);

/**
 * @brief 擦除整个芯片
 */
void W25Q_Erase_Chip(void);

/**
 * @brief 向指定页面写入数据（清理模式）
 * @param page 页面号
 * @param offset 页面内偏移量
 * @param size 写入数据大小
 * @param data 写入数据指针
 */
void W25Q_Write_Page_Clean(uint32_t page, uint16_t offset, uint32_t size, uint8_t *data);

/**
 * @brief 向指定页面写入数据
 * @param page 页面号
 * @param offset 页面内偏移量
 * @param size 写入数据大小
 * @param data 写入数据指针
 */
void W25Q_Write(uint32_t page, uint16_t offset, uint32_t size, uint8_t *data);

/**
 * @brief 读取指定地址的字节数据
 * @param Addr 读取地址
 * @return uint8_t 读取的字节数据
 */
uint8_t W25Q_Read_Byte(uint32_t Addr);

/**
 * @brief 向指定地址写入字节数据
 * @param Addr 写入地址
 * @param data 写入的字节数据
 */
void W25Q_Write_Byte(uint32_t Addr, uint8_t data);

/**
 * @brief 向指定位置写入浮点数
 * @param page 页面号
 * @param offset 页面内偏移量
 * @param data 浮点数数据
 */
void W25Q_Write_NUM(uint32_t page, uint16_t offset, float data);

/**
 * @brief 从指定位置读取浮点数
 * @param page 页面号
 * @param offset 页面内偏移量
 * @return float 读取的浮点数
 */
float W25Q_Read_NUM(uint32_t page, uint16_t offset);

/**
 * @brief 向指定位置写入32位数组
 * @param page 页面号
 * @param offset 页面内偏移量
 * @param size 数组大小
 * @param data 32位数组指针
 */
void W25Q_Write_32B_Array(uint32_t page, uint16_t offset, uint32_t size, uint32_t *data);

/**
 * @brief 从指定位置读取32位数组
 * @param page 页面号
 * @param offset 页面内偏移量
 * @param size 数组大小
 * @param data 32位数组指针
 */
void W25Q_Read_32B_Array(uint32_t page, uint16_t offset, uint32_t size, uint32_t *data);

// 辅助函数
void csLOW(void);
void csHIGH(void);
void SPI_Write(uint8_t *data, uint8_t len);
void SPI_Read(uint8_t *data, uint32_t len);
void W25Q_Delay(uint32_t msec);
uint32_t bytesToWrite(uint32_t size, uint16_t offset);
uint32_t get_tick(void);
void DWT_Delay_Init(void);
void delay_us(uint32_t us);

/**
 * @brief 计算需要修改的字节数
 * @param size 数据大小
 * @param offset 偏移量
 * @return uint32_t 需要修改的字节数
 */
uint32_t bytesToModify(uint32_t size, uint16_t offset);

/**
 * @brief 等待写入完成
 * @return uint8_t 等待状态，0表示成功，非0表示超时
 */
uint8_t W25Q_Wait_For_Write(void);

#endif /* INC_W25QXX_H_ */

/*
16Mb version:
-------------
1 page = 256bytes
8192 pages total
1 sector = 16 pages = 4096 bytes (4KB)
1 block = 128 pages -->  64 blocks
1 block = 256 pages -->  32 blocks    <==

1 block = 128 pages =  8 sectors = 32768 bytes (32KB)
1 block = 256 pages = 16 sectors = 65536 bytes (64KB)

512 sectors
32 blocks
32 blocks × 16 sectors × 16 pages × 256 bytes × 8bits = 16,777,216 bits
32 blocks × 65536 bytes = 2,097,152 bytes = 2MB


64Mb version:
-------------
1 page = 256bytes
32768 pages total
1 sector = 16 pages = 4096 bytes (4KB)
1 block = 128 pages --> 256 blocks
1 block = 256 pages --> 128 blocks    <==

1 block = 128 pages =  8 sectors = 32768 bytes (32KB)
1 block = 256 pages = 16 sectors = 65536 bytes (64KB)

2048 sectors
128 blocks
128 blocks × 16 sectors × 16 pages × 256 bytes × 8bits = 67,108,864 bits
128 blocks × 65536 bytes = 8,388,608 bytes = 8MB


128Mb 版本:
-------------
1 页 = 256字节
总共 65536 页
1 扇区 = 16 页 = 4096 字节 (4KB)
1 块 = 128 页 --> 512 块
1 块 = 256 页 --> 256 块    <==

1 块 = 128 页 =  8 扇区 = 32768 字节 (32KB)
1 块 = 256 页 = 16 扇区 = 65536 字节 (64KB)

4096 扇区
256 块

256 块 × 16 扇区 × 16 页 × 256 字节 × 8位 = 134,217,728 位
256 块 × 65536 字节 = 16,777,216 字节 = 16MB

*/
